package vsqlbuilder.gui.diagram;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;

import vsqlbuilder.logic.Column;
import vsqlbuilder.logic.sql.QueryJoin;

//This class implements IGshape not because it should be done, but just for compatibility with the GTable class inside a diagram
public class GJoinRelation implements IGshape{
    
	private QueryJoin queryJoin;
	
	private boolean visible;

    private int x1;

    private int y1;

    private int x2;

    private int y2;
    
    private static final int LINE_CONTAINS_MAX_TRESHOLD = 7;
    
    private static final int LINE_END_SIZE = 10;
    
    private static final Stroke JOIN_SEL_STROKE = new BasicStroke(2.0f,
    	    BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[] {
    		    3.0f, 3.0f }, 0.0f);
    
    private static final Stroke JOIN_STROKE = new BasicStroke(2.0f);
    
    private GAnchorPoint anchorSource, anchorDestination;
    
	// Indicate if this table is selected by user or not.
	public boolean selected;
    
    /*Begin: Only sketch functions to implement interface IGshape not needed by this class*/
	public Rectangle getRectangle() {
		return null;
	}
	public void move(int x1, int y1, int x2, int y2){
		//not used
	}
	public Column getColumnAt(int x,int y){
		return null;
	}
	/* End: NOT USED interface implementations */
	
	public QueryJoin getShapeLogic(){
		return queryJoin;
	}
	
	public boolean contains(int x, int y) {
		boolean value=false;
		if(distanceToLine(this.x1, this.y1, this.x2, this.y2, x, y)<LINE_CONTAINS_MAX_TRESHOLD){
			return value=true;
		}
		return value;
	}

	
    
    public GJoinRelation(GAnchorPoint source, GAnchorPoint destination, QueryJoin qj){
    	this.anchorSource=source;
    	this.anchorDestination=destination;
    	this.queryJoin=qj;
    	hide();
    }

    public boolean isVisible() {
    	return this.visible;
    }

    public void show(int x, int y) {

    	this.visible = true;
    	this.x1 = this.x2 = x;
    	this.y1 = this.y2 = y;
    }

    public void show(Point p) {

    	this.visible = true;
    	this.x1 = this.x2 = p.x;
    	this.y1 = this.y2 = p.y;
    }
    
    public void hide() {
    	this.visible = true; //DEBUG
    	this.x1 = this.y1 = this.x2 = this.y2 = 0;
    }

    public void resize(int x, int y) {

	this.x2 = x;
	this.y2 = y;
    }

    public Point getSourcePoint(){
    	Point p = new Point(this.x1,this.y1);
    	return p;
    }
    
    public void paint(Graphics2D g) {
    this.recalculateAnchors();
	Color currentColor = g.getColor();
	Stroke currentStroke = g.getStroke();

	g.setColor(Color.WHITE);
	g.drawLine(this.x1, this.y1, this.x2, this.y2);

	g.setColor(Color.BLACK);
	
	if(this.getSelected()){
		g.setStroke(JOIN_SEL_STROKE);
	}else{
		g.setStroke(JOIN_STROKE);
	}
	
	//There is a slightly error in x2 anchor calculation x2 for graphical representation
	if(x1<x2){
		g.drawLine(this.x1+3, this.y1, this.x2, this.y2);		
	}else{
		g.drawLine(this.x1, this.y1, this.x2+3, this.y2);
	}

	//TODO [medium]: replace this rectangle by an  arrow (should be take in care orientation)
	if(x1>=x2){
		g.fillRect(this.x2+3, this.y2-(LINE_END_SIZE/2), LINE_END_SIZE, LINE_END_SIZE);
	}else{
		g.fillRect(this.x2-LINE_END_SIZE, this.y2-(LINE_END_SIZE/2), LINE_END_SIZE, LINE_END_SIZE);
	}

	g.setStroke(currentStroke);
	g.setColor(currentColor);
    }
    
    public void recalculateAnchors(){
    	this.anchorSource.CalculateLocations();   //TEST This two lines to check if don't introduce new errors
    	this.anchorDestination.CalculateLocations();
    	if(this.anchorSource.getOwnerGTableMiddleX()<=this.anchorDestination.getOwnerGTableMiddleX()){
    		//Set Source
    		this.x1=this.anchorSource.getRightLocation().x;
    		this.y1=this.anchorSource.getRightLocation().y;
    		//Set Destination
    		this.x2=this.anchorDestination.getLeftLocation().x;
    		this.y2=this.anchorDestination.getLeftLocation().y;
    	}else{
    		//Set Source
    		this.x1=this.anchorSource.getLeftLocation().x;
    		this.y1=this.anchorSource.getLeftLocation().y;
    		//Set Destination
    		this.x2=this.anchorDestination.getRightLocation().x;
    		this.y2=this.anchorDestination.getRightLocation().y;
    	}
    }
    
    
   // setter of Selected property of class to indicate if table is selected or
  	// not
  	public void setSelected(boolean value) {
  		this.selected = value;
  	}

  	// getter of Selected property of class to indicate if table is selected or
  	// not
  	public boolean getSelected() {
  		return this.selected;
  	}

  	
	public static double distanceToLine(double x1, double y1, double x2,
            double y2, double px, double py) {
            // Adjust vectors relative to x1,y1
            // x2,y2 becomes relative vector from x1,y1 to end of segment
            x2 -= x1;
            y2 -= y1;
            // px,py becomes relative vector from x1,y1 to test point
            px -= x1;
            py -= y1;
            double dotprod = px * x2 + py * y2;
            double projlenSq;
            if (dotprod <= 0.0) {
                // px,py is on the side of x1,y1 away from x2,y2
                // distance to segment is length of px,py vector
                // "length of its (clipped) projection" is now 0.0
                projlenSq = 0.0;
            } else {
                // switch to backwards vectors relative to x2,y2
                // x2,y2 are already the negative of x1,y1=>x2,y2
                // to get px,py to be the negative of px,py=>x2,y2
                // the dot product of two negated vectors is the same
                // as the dot product of the two normal vectors
                px = x2 - px;
                py = y2 - py;
                dotprod = px * x2 + py * y2;
                if (dotprod <= 0.0) {
                    // px,py is on the side of x2,y2 away from x1,y1
                    // distance to segment is length of (backwards) px,py vector
                    // "length of its (clipped) projection" is now 0.0
                    projlenSq = 0.0;
                } else {
                    // px,py is between x1,y1 and x2,y2
                    // dotprod is the length of the px,py vector
                    // projected on the x2,y2=>x1,y1 vector times the
                    // length of the x2,y2=>x1,y1 vector
                    projlenSq = dotprod * dotprod / (x2 * x2 + y2 * y2);
                }
            }
            // Distance to line is now the length of the relative point
            // vector minus the length of its projection onto the line
            // (which is zero if the projection falls outside the range
            //  of the line segment).
            double lenSq = px * px + py * py - projlenSq;
            if (lenSq < 0) {
                lenSq = 0;
            }
            return Math.sqrt(lenSq);
        }
  	
  	
  	
}
